#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <pcap/pcap.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <endian.h>
#include "src/types.h"

#define SNAP_LEN 65535
#define P_ICMP 	1
#define P_UDP 	17
#define P_TCP	6
#define ETHERNET_SIZE 14

typedef struct ether_header ether_header_t;
typedef struct ip ip_t;
typedef struct icmp icmp_t;
typedef struct tcphdr tcphdr_t;
typedef struct udphdr udphdr_t;

void processIP(const struct ip *ip){
	printf("IP Found\n");
	printf("Origem: %-15s Destino: %-15s\n", inet_ntoa(ip->ip_src), inet_ntoa(ip->ip_dst));
}

void processTCP(const struct tcphdr *h, const u_char *p){
	printf("TCP\n");
	printf("Source: %010d Destination:%010d\n", ntohs(h->source), ntohs(h->dest));
	return;
}

void processUDP(const struct udphdr *h, const u_char *p){
	printf("UDP Found\n");
	return;
}

void processICMP(const icmp_t *h, const u_char *p){
	printf("ICMP Found\n");
	printf("Code: %d ", h->icmp_code);
	printf("Type: %d\n", h->icmp_type);
	return;
}

void processEthernet(const struct ether_header *h){

}

void unpack(const u_char *packet, const struct pcap_pkthdr *h){
	const ip_t				*ip    = (const ip_t*)(packet + sizeof(struct ether_header));
	const ether_header_t 	*eh_h  = (const ether_header_t*)packet;
	const icmp_t 	 		*icmp  = NULL;
	const tcphdr_t 		 	*tcp   = NULL;
	const udphdr_t 		 	*udp   = NULL;
	const u_char 		 	*p 	   = NULL;
    const long unsigned int	offset = (const long unsigned int)packet + ETHERNET_SIZE + ip->ip_hl*4;

	switch (ip->ip_p) {
		case P_TCP:
			tcp = (const tcphdr_t*)(offset);
			processTCP(tcp, p);
			break;

		case P_ICMP:
			icmp = (const icmp_t *)(offset);
			processICMP(icmp, NULL);
			break;

		case P_UDP:
			udp = (const udphdr_t*)(ip+sizeof(ip_t));
			p = (const u_char*)ip+sizeof(ip_t)+sizeof(udphdr_t);
//			processUDP(udp, p);
			break;

		default:
			printf("No Protocol found: %d\n", ip->ip_p);
			break;
	}

}

void processador(u_char *contador, const struct pcap_pkthdr *pheader, const u_char *packet){
	unpack(packet, pheader);
	return;
}

int main(int argc, char **argv) {
	char err_buf[PCAP_ERRBUF_SIZE];
	int contador = 0;
	pcap_t *session = NULL;


	memcpy(err_buf, "0", PCAP_ERRBUF_SIZE);

	printf("Inciando sessão de captura\n");

	session = pcap_open_live(argv[1], SNAP_LEN, 1000, 1, err_buf);

	if ( session == NULL ){
		printf("Erro ao iniciar sessão %s\n", err_buf);
		exit(1);
	}

	if ( err_buf[0] != '0'){
		printf("Aviso: %s", err_buf);
	}

	printf("Despositivo de rede: %s\n", argv[1]);

	pcap_loop(session, -1, processador, (u_char *)&contador);

	return 0;
}

